/*=============================================================================
PostProcessMotionBlur : camera/object motion blur post processing
Copyright (c) 2001 Crytek Studios. All Rights Reserved.

Revision history:
* 23/02/2005: Re-factored/Converted to CryEngine 2.0 by Tiago Sousa

* 15/04/2010: Started refactoring
* todo:
*	- merge HDR/LDR approach all 1 one (remove copy/paste)
*	- merge version of camera + object motion blur all 1 one

=============================================================================*/

#include "StdAfx.h"
#include "DriverD3D.h"
#include "I3DEngine.h"
#include "D3DPostProcess.h"
#include "D3DStereo.h"

#pragma warning(disable: 4244)

CMotionBlur::OMBParamsMap CMotionBlur::m_pOMBData[3];

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void CMotionBlur::SetupObject( CRenderObject *pObj )
{
	assert( pObj );

	uint32 nThreadID = gRenDev->m_RP.m_nFillThreadID;

	if( SRendItem::m_RecurseLevel[nThreadID] > 1 )
		return;

	SRenderObjData *const __restrict pOD =  pObj->GetObjData(nThreadID);

	pObj->m_ObjFlags &= ~FOB_HAS_PREVMATRIX;	

	IRenderNode *pRenderNode = (pOD && pOD->m_pInstance)?(IRenderNode*) pOD->m_pInstance : 0;

	if( pRenderNode && pObj->m_fDistance < CRenderer::CV_r_MotionBlurMaxViewDist)
	{
		const uint32 nFrameID = gRenDev->GetFrameID(false);
		const uint32 ObjID		=	(reinterpret_cast<size_t>(pRenderNode)+pOD->m_nObjID);
		const uint32 nObjFrameWriteID = (nFrameID)%3;		
		OMBParamsMapItor	it	= m_pOMBData[nObjFrameWriteID].find( ObjID );		
		if( it != m_pOMBData[nObjFrameWriteID].end() )
		{
			// if all good, get previous buffered frame
			const uint32 nObjPrevFrameID = (nFrameID - 1)%3;
			OMBParamsMapItor	itPrev	= m_pOMBData[nObjPrevFrameID].find( ObjID );

			if( itPrev != m_pOMBData[nObjPrevFrameID].end() )
			{
				SObjMotionBlurParams *pWriteObjMBData = &it->second;
				SObjMotionBlurParams *pPrevObjMBData = &itPrev->second;
				pWriteObjMBData->mObjToWorld = pObj->m_II.m_Matrix;
#ifdef SEG_WORLD
				pPrevObjMBData->mObjToWorld.SetTranslation(pPrevObjMBData->mObjToWorld.GetTranslation() + gRenDev->m_vSegmentedWorldOffset);
#endif
				const float fThreshold = 0.01f;// :1e-6f ;
				if( !pPrevObjMBData->mObjToWorld.IsEquivalent( pWriteObjMBData->mObjToWorld, fThreshold )|| (pObj->m_ObjFlags & (FOB_NEAREST|FOB_HAS_PREVSKINXFORM))) 							
					pObj->m_ObjFlags |= FOB_HAS_PREVMATRIX;				

				pWriteObjMBData->nFrameUpdateID = nFrameID;
				pWriteObjMBData->pRenderObj = pObj;
				
				return;
			}			
		}

		m_pOMBData[nObjFrameWriteID].insert(OMBParamsMap::value_type(ObjID, SObjMotionBlurParams(pObj, pObj->m_II.m_Matrix, nFrameID)));
	}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

const Matrix34A& CMotionBlur::GetPrevObjToWorldMat( CRenderObject *pObj )
{
	assert( pObj );

	if( pObj->m_ObjFlags & FOB_HAS_PREVMATRIX )
	{
		uint32 nThreadID = gRenDev->m_RP.m_nProcessThreadID;
		SRenderObjData *const __restrict pOD = pObj->GetObjData(nThreadID);

		const uint32 ObjID = pOD ? reinterpret_cast<size_t>(pOD->m_pInstance) + pOD->m_nObjID : 0;
		const uint32 nFrameID = gRenDev->GetFrameID(false);
		const uint32 nObjFrameReadID = (nFrameID - 1 )%3;

		OMBParamsMapItor it	= m_pOMBData[nObjFrameReadID].find(ObjID);
		if( it != m_pOMBData[nObjFrameReadID].end() )
		{
			return it->second.mObjToWorld;
		}
	}

	return pObj->m_II.m_Matrix;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void CMotionBlur::OnBeginFrame()
{
	assert(!gRenDev->m_pRT || gRenDev->m_pRT->IsMainThread());

	const uint32 nDiscardThreshold = 60;
	const uint32 nFrameID = gRenDev->GetFrameID(false);
	const uint32 nObjFrameReadID = nFrameID % 3;

	OMBParamsMap& map = m_pOMBData[nObjFrameReadID];
	for (OMBParamsMapItor it = map.begin(), itEnd = map.end(); it != itEnd;)
	{
		const SObjMotionBlurParams& objMBData = (*it).second;
		if ((nFrameID - objMBData.nFrameUpdateID) > nDiscardThreshold)
			map.erase(it++);
		else
			++it;
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

bool CD3D9Renderer::FX_MotionBlurScene(bool bEnable)
{
  bool bQualityCheck = CPostEffectsMgr::CheckPostProcessQuality( eRQ_Medium, eSQ_Medium );

#if defined( XENON ) || defined( PS3 )
	bQualityCheck = true;
#endif

	if( !bQualityCheck )
    return false;

	if( s_AllowMotionBlur < 2)
    return false;

  if(bEnable)
  {
		gcpRendD3D->Set2DMode(false, 1, 1);     

    GetUtils().Log(" +++ Begin object motion blur scene +++ \n"); 

    // Re-use scene target rendertarget for velocity buffer
    RT_SetViewport(0, 0, CTexture::s_ptexSceneTarget->GetWidth(), CTexture::s_ptexSceneTarget->GetHeight());                        

    m_RP.m_PersFlags2 |= RBPF2_MOTIONBLURPASS;
  }
  else  
  {     
    FX_ResetPipe();     
    gcpRendD3D->RT_SetViewport(0, 0, gcpRendD3D->GetWidth(), gcpRendD3D->GetHeight());

		m_RP.m_PersFlags2 &= ~RBPF2_MOTIONBLURPASS;

		gcpRendD3D->Set2DMode(true, 1, 1);     

    GetUtils().Log(" +++ End object motion blur scene +++ \n"); 
  }

  return true;
}


////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

bool CMotionBlur::Preprocess()
{
	// Always HDR now
	return false;

	const int nThreadID = gRenDev->m_RP.m_nProcessThreadID;

	//// Motion blur overrides scene normals user for thermal vision - cannot have both enabled at same time
	//if ( gRenDev->IsCustomRenderModeEnabled(eRMF_THERMALVISION) )
//		return false;


  bool bQualityCheck = CPostEffectsMgr::CheckPostProcessQuality( eRQ_Medium, eSQ_Medium );
	if( !bQualityCheck || !CRenderer::s_AllowMotionBlur)
    return false;

  bool bActive = !CRenderer::CV_r_HDRRendering;
	Matrix44A pPrevView = CMotionBlur::GetPrevView();

  //if( !bActive )
  //  pPrevView = GetUtils().m_pView;

  m_fRotationAcc *= max(1.f - gEnv->pTimer->GetFrameTime(), 0.f);    
  if (CTexture::s_ptexSceneTarget == NULL || !bActive)
    return false;

  Matrix44A pCurrView( GetUtils().m_pView  ), pCurrProj( GetUtils().m_pProj );  
  
  m_nSamplesInfo = 0;
  m_fRotSamplesEst = m_fRotationAcc;
  m_fTransSamplesEst = 0;

  const CCamera &currentCamera = gcpRendD3D->GetCamera();

  // If it's a new camera, disable motion blur for the first frame
  if (currentCamera.IsJustActivated())
  {
    int32 nR = SRendItem::m_RecurseLevel[nThreadID]-1;
#   ifndef _RELEASE
    if (nR<0) __debugbreak();
#   endif 
    int32 nRenderView = SRendItem::m_RenderView[nThreadID][nR];
    gRenDev->m_CameraMatrixPrev/*[nThreadID][ min(1, nRenderView) ]*/= gcpRendD3D->m_CameraMatrix;
  }

  // No movement, skip motion blur - why no IsEquivalent for 4x4...
  Vec3 pCurr0 = pCurrView.GetRow(0), pCurr1 = pCurrView.GetRow(1), pCurr2 = pCurrView.GetRow(2), pCurr3 = pCurrView.GetRow(3);
  Vec3 pPrev0 = pPrevView.GetRow(0), pPrev1 = pPrevView.GetRow(1), pPrev2 = pPrevView.GetRow(2), pPrev3 = pPrevView.GetRow(3);
  if( pCurr0.IsEquivalent( pPrev0, 0.025f) && pCurr1.IsEquivalent( pPrev1, 0.025f) && pCurr2.IsEquivalent( pPrev2, 0.025f) && pCurr3.IsEquivalent( pPrev3, 0.3f) ) 
  {
   // pPrevView = pCurrView;
//    return false;
  }

  return bActive;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void CMotionBlur::MotionVectorsGen()
{
	uint64 nSaveFlagsShader_RT = gRenDev->m_RP.m_FlagsShader_RT;
	gRenDev->m_RP.m_FlagsShader_RT &= ~(g_HWSR_MaskBit[HWSR_SAMPLE0]|g_HWSR_MaskBit[HWSR_SAMPLE1]|g_HWSR_MaskBit[HWSR_SAMPLE2]);

	PROFILE_LABEL_PUSH( "MOTION VECTORS GEN" );

	const bool bAllowObjMotionBlur = CRenderer::CV_r_MotionBlur >= 2;

	gRenDev->m_cEF.mfRefreshSystemShader("MotionBlur", CShaderMan::m_shPostMotionBlur);

	int iTempX, iTempY, iWidth, iHeight;
	gcpRendD3D->GetViewport(&iTempX, &iTempY, &iWidth, &iHeight);

	Matrix44A mViewProjPrev = CMotionBlur::GetPrevView() * (GetUtils().m_pProj *m_mScaleBias);      
	mViewProjPrev.Transpose();
	 
	// Render objects velocities (camera velocity inlined in final apply pass - faster ~0.4ms)
	gcpRendD3D->FX_PushRenderTarget(0,  CTexture::s_ptexBackBuffer, &gcpRendD3D->m_DepthBufferOrig); //GetUtils().GetDepthSurface( CTexture::s_ptexSceneNormalsMap )
	gcpRendD3D->RT_SetViewport(0, 0, CTexture::s_ptexBackBuffer->GetWidth(), CTexture::s_ptexBackBuffer->GetHeight());        

	// temporary clear/fix - try avoiding this
	ColorF clearCol(127.0f/255.0f, 127.0f/255.0f, 0.0f, 0);		
	int32 clearFlags = FRT_CLEAR_COLOR|FRT_CLEAR_IMMEDIATE;
	gcpRendD3D->EF_ClearBuffers(clearFlags, &clearCol);

	if( bAllowObjMotionBlur )
	{
		// Check for moving geometry
		uint32 nBatchMask = SRendItem::BatchFlags(EFSLIST_GENERAL, gRenDev->m_RP.m_pRLD);
		nBatchMask |= SRendItem::BatchFlags(EFSLIST_TRANSP, gRenDev->m_RP.m_pRLD);
		nBatchMask |= SRendItem::BatchFlags(EFSLIST_SKIN, gRenDev->m_RP.m_pRLD);
		if (nBatchMask & FB_MOTIONBLUR)
		{
			PROFILE_LABEL_PUSH( "OBJECT MOTION VECTOR GEN" );

			CRendElementBase *pPrevRE = gRenDev->m_RP.m_pRE;			
			gRenDev->m_RP.m_pRE = NULL;





			gcpRendD3D->FX_ProcessPostRenderLists(FB_MOTIONBLUR);

			gRenDev->m_RP.m_pRE = pPrevRE;





			PROFILE_LABEL_POP( "OBJECT MOTION VECTOR GEN" );
		}				
	}

	PROFILE_LABEL_POP( "MOTION VECTORS GEN" );

	gcpRendD3D->FX_PopRenderTarget(0); 
	gcpRendD3D->RT_SetViewport(iTempX, iTempY, iWidth, iHeight);     

	gRenDev->m_RP.m_FlagsShader_RT = nSaveFlagsShader_RT;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

float CMotionBlur::GetShutterSpeed()
{
	// Re-normalize frametime to account for time scaling
	float fCurrFrameTime = gEnv->pTimer->GetFrameTime();	
	float fTimeScale = gEnv->pTimer->GetTimeScale();
	if (fabs(fTimeScale-1.f) < FLT_EPSILON)
	{
		fTimeScale = max(0.0001f, fTimeScale);
		fCurrFrameTime /= fTimeScale;
	}

	// Scale down shutter speed a bit. Default shutter is 0.02, so final result is 0.004 (old default value)
	float fExposureTime = CRenderer::CV_r_MotionBlurShutterSpeed;
	float fAlpha = iszero(fCurrFrameTime) ? 0.0f : fExposureTime /fCurrFrameTime;
	if( CRenderer::CV_r_MotionBlurFrameTimeScale )
	{
		float fAlphaScale = iszero(fCurrFrameTime) ? 1.0f : min(1.0f, (1.0f / fCurrFrameTime) / ( 32.0f)); // attenuate motion blur for lower frame rates
		fAlpha *= fAlphaScale;
	}

	return fAlpha;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void CMotionBlur::Render()
{
	// Half resolution motion blur/depth of field

	CPostEffectsMgr *pPostMgr = PostEffectMgr();

	const int nThreadID = gRenDev->m_RP.m_nProcessThreadID;
	gRenDev->m_cEF.mfRefreshSystemShader("MotionBlur", CShaderMan::m_shPostMotionBlur);
 
  PROFILE_LABEL_PUSH( "HALF RES MOTION BLUR DOF" );

	gcpRendD3D->FX_SetActiveRenderTargets();











	const bool bAllowObjMotionBlur = CRenderer::CV_r_MotionBlur >= 2;

	int iTempX, iTempY, iWidth, iHeight;
	gcpRendD3D->GetViewport(&iTempX, &iTempY, &iWidth, &iHeight);
	
	float fAlpha = GetShutterSpeed();

	uint64 nSaveFlagsShader_RT = gRenDev->m_RP.m_FlagsShader_RT;
	gRenDev->m_RP.m_FlagsShader_RT &= ~(g_HWSR_MaskBit[HWSR_SAMPLE0]|g_HWSR_MaskBit[HWSR_SAMPLE1]|g_HWSR_MaskBit[HWSR_SAMPLE2]);

	Matrix44A mViewProjPrev = CMotionBlur::GetPrevView();
	Matrix44 mViewProj = GetUtils().m_pView  ;

















	mViewProjPrev = mViewProjPrev * GetUtils().m_pProj * m_mScaleBias;
	mViewProj = mViewProj * GetUtils().m_pProj * m_mScaleBias;
	mViewProjPrev.Transpose();
	mViewProj.Transpose();
	
	CDepthOfField *pDofRenderTech = (CDepthOfField *)pPostMgr->GetEffect(ePFX_eDepthOfField);
	SDepthOfFieldParams pDofParams = pDofRenderTech->GetParams();
	pDofParams.vFocus.w *= 2.0f;

	// prebake parameters in cpu side
	Vec4 vFocus = pDofParams.vFocus;
	
	if( pDofParams.bGameMode )
	{		
		pDofParams.vFocus.x = 1.0f / (vFocus.z + 1e-6f);
		pDofParams.vFocus.y = - vFocus.y / (vFocus.z + 1e-6f);

		pDofParams.vFocus.z = - 1.0f / (vFocus.w + 1e-6f);
		pDofParams.vFocus.w =  vFocus.x / (vFocus.w + 1e-6f); 
	}
	else
	{
		pDofParams.vFocus.x = 1.0f / (vFocus.y + 1e-6f);
		pDofParams.vFocus.y = - vFocus.z / (vFocus.y + 1e-6f);

		pDofParams.vFocus.z = 1.0f / (vFocus.x + 1e-6f);
		pDofParams.vFocus.w = - vFocus.z / (vFocus.x + 1e-6f);
	}

	Vec4 vDofParams0 =pDofParams.vFocus;

	const float fNearestDofScaleBoost = ( pDofParams.bGameMode )? CRenderer::CV_r_dofMinZBlendMult : 1.0f;
	Vec4 vDofParams1 = Vec4(CRenderer::CV_r_dofMinZ + pDofParams.vMinZParams.x, CRenderer::CV_r_dofMinZScale+ pDofParams.vMinZParams.y, fNearestDofScaleBoost, vFocus.w);

	bool bDofEnabled = CRenderer::CV_r_dof>0;
	EShaderQuality nShaderQuality = gRenDev->EF_GetShaderQuality(eST_PostProcess);
 
  PROFILE_SHADER_START
  
	////////////////////////////////////////////////////////////////////////////////////////////////////////////

	MotionVectorsGen();

	if( nShaderQuality>=eSQ_High && CRenderer::CV_r_MotionBlur )
	{
		// For hi specs perform velocity buffer dilation (store blending mask directly in alpha channel)
		GetUtils().StretchRect(CTexture::s_ptexBackBuffer, CTexture::s_ptexBackBufferScaled[0], true);
		GetUtils().TexBlurGaussian(CTexture::s_ptexBackBufferScaled[0], 1, 1.0f, 1.0f, false, NULL, true, CTexture::s_ptexBackBufferScaledTemp[0]);
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////////////
	
	CTexture *pSceneSrc = CTexture::s_ptexHDRTargetScaledTmp[0];
	CTexture *pSceneTmp = CTexture::s_ptexHDRTargetScaled[0];
	CTexture *pSceneTmpSec = CTexture::s_ptexHalfResCompositionMask;









		
#if defined (DIRECT3D10) && !defined(PS3) && !defined(CAFE)
	const uint32 nBokehMeshWidth = CTexture::s_ptexHDRDofLayers[0]->GetWidth();
	const uint32 nBokehMeshHeight = CTexture::s_ptexHDRDofLayers[0]->GetHeight();

	if( nShaderQuality == eSQ_VeryHigh && ( !m_pBokehQuadMesh || m_nBokehMeshWidth != nBokehMeshWidth || m_nBokehMeshHeight != nBokehMeshHeight ) )
	{
		m_nBokehMeshWidth = nBokehMeshWidth;
		m_nBokehMeshHeight = nBokehMeshHeight;

		const uint32 nBokehQuadsCount = nBokehMeshWidth * nBokehMeshHeight;
		float fRecipW = 1.0f / (float) nBokehMeshWidth;
		float fRecipH = 1.0f / (float) nBokehMeshHeight;

		SVF_P3S_C4B_T2S* pBokehQuads = new SVF_P3S_C4B_T2S[nBokehQuadsCount];
		for( uint32 y = 0; y < nBokehMeshHeight; ++y)
		{
			for( uint32 x = 0; x < nBokehMeshWidth; ++x)
			{
				pBokehQuads[y * nBokehMeshWidth + x].xyz = Vec3( ((float) x ) , ((float) y ), 0.0f );
			}
		}

		m_pBokehQuadMesh = gRenDev->CreateRenderMeshInitialized(pBokehQuads, nBokehQuadsCount, eVF_P3S_C4B_T2S, 0, 0, prtTriangleList, "BokehMesh", "BokehMesh");

		delete [] pBokehQuads;
	}
#endif

	// Override draw near z range for camera motion blur (avoid blurring when in vehicles etc)
	const float customNearZRange = clamp_tpl<float>(m_pDrawNearZRangeOverride->GetParam(),0.0f,1.0f);
	Vec4 vMotionBlurParams = Vec4(customNearZRange,0.0f,0.0f,0.0f);


	if (m_pBokehQuadMesh && nShaderQuality == eSQ_VeryHigh ) 
	{
		// DX11/hi quality path for motion blur/ dof

		gcpRendD3D->Set2DMode(false, 1, 1);

    if( CRenderer::CV_r_MotionBlur )
		{
			PROFILE_LABEL_PUSH( "MOTION VECTOR APPLY" );

			{
				// set object alpha mask / render to non-multisampled target in case of MSAA enabled
				{
#ifdef SUPPORTS_MSAA
          CTexture::s_ptexHDRTarget->SetResolved(true);
          CTexture::s_ptexHDRTarget->SetUseMultisampledRTV(false);
#endif

					gcpRendD3D->Set2DMode(true, 1, 1);

					gcpRendD3D->FX_PushRenderTarget(0, CTexture::s_ptexHDRTarget, 0);
					static CCryNameTSCRC pMotionBlurMaskTechName("MotionBlurMask");
					PostProcessUtils().ShBeginPass(CShaderMan::m_shPostMotionBlur, pMotionBlurMaskTechName, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
					gRenDev->FX_SetState( GS_NODEPTHTEST|GS_COLMASK_A );
					GetUtils().SetTexture(CTexture::s_ptexBackBuffer, 0, FILTER_POINT);
					PostProcessUtils().DrawFullScreenQuad(CTexture::s_ptexBackBuffer->GetWidth(), CTexture::s_ptexBackBuffer->GetHeight());
					PostProcessUtils().ShEndPass();	
					gcpRendD3D->FX_PopRenderTarget(0);
					
					gcpRendD3D->Set2DMode(false, 1, 1);

#ifdef SUPPORTS_MSAA
          CTexture::s_ptexHDRTarget->SetResolved(true);
          CTexture::s_ptexHDRTarget->SetUseMultisampledRTV(true);
#endif
				}

				gcpRendD3D->FX_PushRenderTarget(0, CTexture::s_ptexSceneTarget, 0);
				gcpRendD3D->RT_SetViewport(0, 0, CTexture::s_ptexSceneTarget->GetWidth(), CTexture::s_ptexSceneTarget->GetHeight());        

				float fVectorsScale = m_pVectorsScale->GetParam() * m_pUserVectorsScale->GetParam();	
				float fCurrVectorsScale = fVectorsScale*fAlpha ;
				Vec4 vParams = Vec4(fCurrVectorsScale, fCurrVectorsScale, 0, fCurrVectorsScale) ;            
				Vec4 cBlurVec = PostEffectMgr()->GetByNameVec4("Global_DirectionalBlur_Vec") * 0.01f;   

				// Enable/disable dof combination if required
				gRenDev->m_RP.m_FlagsShader_RT &= ~(g_HWSR_MaskBit[HWSR_SAMPLE0]|g_HWSR_MaskBit[HWSR_SAMPLE1]|g_HWSR_MaskBit[HWSR_SAMPLE2]|g_HWSR_MaskBit[HWSR_SAMPLE4]|g_HWSR_MaskBit[HWSR_SAMPLE5]);
				if ( bDofEnabled )  
					gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0] | ((pDofParams.bGameMode)?g_HWSR_MaskBit[HWSR_SAMPLE1]:0);

				// enable merged radial blurring combination (1 less resolve, 1 less fulscreen pass - at least around 1ms save)
				const float fParamThresholdBackCompatibility = 0.09f;
				if( m_pRadBlurAmount->GetParam() > fParamThresholdBackCompatibility )
					gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE5];

				// Allow disabling motion blur (set offsets scale to 0, we still might use dof)
				if( CRenderer::CV_r_MotionBlur )
					gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE2];

				GetUtils().ShBeginPass(CShaderMan::m_shPostMotionBlur, m_pMotionBlurRefTechName, FEF_DONTSETTEXTURES|FEF_DONTSETSTATES);

				gcpRendD3D->FX_SetState(GS_NODEPTHTEST);    

				CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pViewProjParam, (Vec4 *)mViewProj.GetData(), 4); 
				CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pViewProjPrevParam, (Vec4 *)mViewProjPrev.GetData(), 4); 
				
				static CCryNameR pViewProjInvParam("mViewProjInv");
				Matrix44 mViewProjInv = mViewProj;
				mViewProjInv.Invert();
				CShaderMan::m_shPostMotionBlur->FXSetPSFloat(pViewProjInvParam, (Vec4 *)mViewProjInv.GetData(), 4);  

				CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pDiretionalBlurParamName, &cBlurVec, 1);
				CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pMotionBlurParamsName, &vParams, 1);

				// set radial blur parameters as well
				float fAmount = clamp_tpl<float>(m_pRadBlurAmount->GetParam(), 0.0f, 1.0f);
				float fScreenPosX = clamp_tpl<float>(m_pRadBlurScreenPosX->GetParam(), 0.0f, 1.0f);
				float fScreenPosY = clamp_tpl<float>(m_pRadBlurScreenPosY->GetParam(), 0.0f, 1.0f);
				float fRadius = m_pRadBlurRadius->GetParam();
				fRadius = 1.0f / clamp_tpl<float>(fRadius*2.0f, 0.001f, 1.0f); 

				Vec4 vRadBlurParam = Vec4(fScreenPosX, fScreenPosY, fRadius, 0.01f * fAmount);
				CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pRadBlurParamName, &vRadBlurParam, 1);

				GetUtils().SetTexture(CTexture::s_ptexHDRTarget, 0, FILTER_LINEAR); // must use point for depth in alpha
				GetUtils().SetTexture(CTexture::s_ptexBackBufferScaled[0], 1, FILTER_LINEAR);//FILTER_LINEAR);
				GetUtils().SetTexture(CTexture::s_ptexZTarget, 2, FILTER_POINT);

				// enable merged radial blurring mask
				CTexture *pRadBlurMaskTex = const_cast<CTexture *> (static_cast<CParamTexture*>(m_pRadialBlurMaskTex)->GetParamTexture());
				if( m_pRadBlurAmount->GetParam() > fParamThresholdBackCompatibility && pRadBlurMaskTex)
					GetUtils().SetTexture(pRadBlurMaskTex, 4);
				else
					GetUtils().SetTexture(CTexture::s_ptexWhite, 4);

				SD3DPostEffectsUtils::DrawFullScreenQuadWPOS(CTexture::s_ptexSceneTarget->GetWidth(),	CTexture::s_ptexSceneTarget->GetHeight());

				GetUtils().ShEndPass(); 

				gcpRendD3D->FX_PopRenderTarget(0); 			
			}

			PROFILE_LABEL_POP( "MOTION VECTOR APPLY" );
		}

		// restore previous frame data
		gRenDev->m_RP.m_FlagsShader_RT = nSaveFlagsShader_RT;

		gcpRendD3D->Set2DMode(true, 1, 1);

		if( bDofEnabled )
		{
			GetUtils().StretchRect(CTexture::s_ptexSceneTarget, CTexture::s_ptexHDRTargetScaledTmp[0], false, false, false, true); // 0.25
			pSceneSrc =CTexture::s_ptexHDRTargetScaledTmp[0];

			///////////////////////////////////////////////////////////////////////////////////////////////////
			// Apply depth of field / masked blur

			const uint32 nWidth = CTexture::s_ptexHDRDofLayers[0]->GetWidth();
			const uint32 nHeight = CTexture::s_ptexHDRDofLayers[0]->GetHeight();
			const uint32 nBokehQuadsCount = nWidth * nHeight;
			
			PROFILE_LABEL_PUSH( "QUAD BOKEH DOF" );

			gcpRendD3D->FX_PushRenderTarget(0, CTexture::s_ptexHDRDofLayers[0], NULL);
			gcpRendD3D->FX_PushRenderTarget(1, CTexture::s_ptexHDRDofLayers[1], NULL);
			gcpRendD3D->RT_SetViewport(0, 0, CTexture::s_ptexHDRDofLayers[0]->GetWidth(), CTexture::s_ptexHDRDofLayers[0]->GetHeight());        

			ColorF clearCol(0, 0, 0, 0);		
			int32 clearFlags = FRT_CLEAR_COLOR|FRT_CLEAR_IMMEDIATE;
			gcpRendD3D->EF_ClearBuffers(clearFlags, &clearCol); 

			gRenDev->m_RP.m_FlagsShader_RT &= ~g_HWSR_MaskBit[HWSR_SAMPLE0];
			if( CRenderer::CV_r_DofBokehQuality )
				gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];

			static CCryNameTSCRC pszTech("DofBokehRef");
			CShaderMan::m_shPostMotionBlur->FXSetTechnique(pszTech);   
			uint32 nPasses(0);
			CShaderMan::m_shPostMotionBlur->FXBegin(&nPasses, FEF_DONTSETTEXTURES|FEF_DONTSETSTATES);
			CShaderMan::m_shPostMotionBlur->FXBeginPass(0);

			int32 nRenderState = GS_NODEPTHTEST | GS_BLSRC_ONE | GS_BLDST_ONE;      
			gcpRendD3D->SetCullMode( R_CULL_BACK );  
			gcpRendD3D->FX_SetState( nRenderState );

			// bind vertex textures for dof splats
			pSceneSrc->SetVertexTexture(true);
			GetUtils().SetTexture(pSceneSrc, 0, FILTER_POINT);      
			CTexture::s_ptexZTarget->SetVertexTexture(true);
			GetUtils().SetTexture(CTexture::s_ptexZTarget, 1, FILTER_POINT);

			pSceneSrc->SetVertexTexture(false);
			GetUtils().SetTexture(pSceneSrc, 0, FILTER_POINT);      
			CTexture::s_ptexZTarget->SetVertexTexture(false);
			GetUtils().SetTexture(CTexture::s_ptexZTarget, 1, FILTER_POINT);
			GetUtils().SetTexture(CTexture::s_ptexGrainFilterMap, 2, FILTER_POINT);
			if( m_pBokehShape )
				GetUtils().SetTexture(m_pBokehShape, 3, FILTER_TRILINEAR);	

      //float2 v1080p = float2(1920.0, 1080.0);
      //float2 vSizeRatio = vMainScreenSize / v1080p;
      //OUT.QuadPos.xy *= vSizeRatio;

      //float fAspectRatio = (v1080p.x / v1080p.y) * (vScreenSizeFullRes.y / vScreenSizeFullRes.x);
      //OUT.QuadPos.x *= fAspectRatio;

			static CCryNameR pAspectRatioName("vAspectRatio");
			Vec4 vAspectRatioMul = Vec4(CTexture::s_ptexHDRTarget->GetWidth(), CTexture::s_ptexHDRTarget->GetHeight(), 1.0f, 1.0f);
			const float fAspectRatio = (1920.0f / 1080.0f) * (vAspectRatioMul.y / vAspectRatioMul.x);
			vAspectRatioMul = Vec4( fAspectRatio * (vAspectRatioMul.x / 1920.0f), vAspectRatioMul.y / 1080.0f, 1.0f, 1.0f);
			CShaderMan::m_shPostMotionBlur->FXSetVSFloat(pAspectRatioName, &vAspectRatioMul, 1);

			CShaderMan::m_shPostMotionBlur->FXSetVSFloat(m_pDofFocusParam0Name, &vDofParams0, 1);
			CShaderMan::m_shPostMotionBlur->FXSetVSFloat(m_pDofFocusParam1Name, &vDofParams1, 1);            
			CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pDofFocusParam0Name, &vDofParams0, 1);
			CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pDofFocusParam1Name, &vDofParams1, 1);

			gcpRendD3D->FX_Commit();
			if (!FAILED(gcpRendD3D->FX_SetVertexDeclaration(0, eVF_P3S_C4B_T2S)))
			{
				int offset(0);
				CRenderMesh* pBokehQuadMesh((CRenderMesh*) m_pBokehQuadMesh);
				pBokehQuadMesh->CheckUpdate(pBokehQuadMesh->_GetVertexFormat(), 0);
				D3DVertexBuffer *pVB = gcpRendD3D->m_DevBufMan.GetD3DVB(pBokehQuadMesh->_GetVBStream(VSF_GENERAL).m_nID, &offset);
				gcpRendD3D->FX_SetVStream(0, pVB, offset, pBokehQuadMesh->GetStreamStride(VSF_GENERAL));
				gcpRendD3D->FX_SetIStream(0);

				gcpRendD3D->FX_DrawPrimitive(eptPointList, 0, nBokehQuadsCount);
			}

			CShaderMan::m_shPostMotionBlur->FXEndPass();
			CShaderMan::m_shPostMotionBlur->FXEnd();

			pSceneSrc->SetVertexTexture(false);
			CTexture::s_ptexZTarget->SetVertexTexture(false);

			gcpRendD3D->FX_PopRenderTarget(1);	
			gcpRendD3D->FX_PopRenderTarget(0);	

			PROFILE_LABEL_POP( "QUAD BOKEH DOF" );

	
			PROFILE_LABEL_PUSH( "BOKEH DOF COMPOSE" );

			// Apply normalization+composition pass
			gcpRendD3D->FX_PushRenderTarget(0,  CTexture::s_ptexHDRTarget, NULL);

			GetUtils().ShBeginPass(CShaderMan::m_shPostMotionBlur, m_pRefDofMBNormalize, FEF_DONTSETTEXTURES|FEF_DONTSETSTATES);
			gcpRendD3D->SetCullMode( R_CULL_BACK );  
			gcpRendD3D->FX_SetState( GS_NODEPTHTEST | GS_BLSRC_SRCALPHA | GS_BLDST_ONEMINUSSRCALPHA );
			CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pDofFocusParam0Name, &vDofParams0, 1);
			CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pDofFocusParam1Name, &vDofParams1, 1);

			GetUtils().SetTexture(CTexture::s_ptexHDRDofLayers[0], 0, FILTER_LINEAR);
			GetUtils().SetTexture(CTexture::s_ptexHDRDofLayers[1], 1, FILTER_LINEAR);
			GetUtils().SetTexture(CTexture::s_ptexSceneTarget, 2, FILTER_POINT);
			GetUtils().SetTexture(CTexture::s_ptexZTarget, 3, FILTER_POINT);


			SD3DPostEffectsUtils::DrawFullScreenQuad(CTexture::s_ptexHDRTarget->GetWidth(),	CTexture::s_ptexHDRTarget->GetHeight());

			GetUtils().ShEndPass(); 
			
			gcpRendD3D->FX_PopRenderTarget(0);	

			PROFILE_LABEL_POP( "BOKEH DOF COMPOSE" );

			// Update downscaled version
      CTexture::s_ptexHDRTarget->SetResolved(true);
			PostProcessUtils().StretchRect(CTexture::s_ptexHDRTarget, pSceneTmp, false, false, false, false, false, true);
		}
		else
		{
      // reminder: is this working with msaa at all ?
			GetUtils().StretchRect(CTexture::s_ptexSceneTarget, CTexture::s_ptexHDRTarget); 
			PostProcessUtils().StretchRect(CTexture::s_ptexHDRTarget, pSceneTmp); 
		}

		gcpRendD3D->Set2DMode(false, 1, 1);
	}
	else
	{

		PROFILE_LABEL_PUSH( "MOTION VECTOR APPLY" );

	gcpRendD3D->Set2DMode(false, 1, 1);

	gcpRendD3D->FX_PushRenderTarget(0, pSceneTmp, 0);




	gcpRendD3D->RT_SetViewport(0, 0, pSceneTmp->GetWidth(), pSceneTmp->GetHeight());        

	float fVectorsScale = m_pVectorsScale->GetParam() * m_pUserVectorsScale->GetParam();	
	float fCurrVectorsScale = fVectorsScale*fAlpha;
	Vec4 vParams = Vec4(fCurrVectorsScale, fCurrVectorsScale, 0, fCurrVectorsScale) ;            
	Vec4 cBlurVec = PostEffectMgr()->GetByNameVec4("Global_DirectionalBlur_Vec") * 0.01f;   

	// Enable/disable dof combination if required
	gRenDev->m_RP.m_FlagsShader_RT &= ~(g_HWSR_MaskBit[HWSR_SAMPLE0]|g_HWSR_MaskBit[HWSR_SAMPLE1]|g_HWSR_MaskBit[HWSR_SAMPLE2]|g_HWSR_MaskBit[HWSR_SAMPLE4]|g_HWSR_MaskBit[HWSR_SAMPLE5]);
	if ( bDofEnabled )  
		gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0] | ((pDofParams.bGameMode)?g_HWSR_MaskBit[HWSR_SAMPLE1]:0);
	
	// enable dof mask 
	if( pDofParams.pMaskTex )
		gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE4];
	
	// enable merged radial blurring combination (1 less resolve, 1 less fulscreen pass - at least around 1ms save)
	const float fParamThresholdBackCompatibility = 0.09f;
	if( m_pRadBlurAmount->GetParam() > fParamThresholdBackCompatibility )
		gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE5];

	// Allow disabling motion blur (set offsets scale to 0, we still might use dof)
	if( CRenderer::CV_r_MotionBlur )
		gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE2];

  GetUtils().ShBeginPass(CShaderMan::m_shPostMotionBlur, m_pMotionBlurDofTechName, FEF_DONTSETTEXTURES|FEF_DONTSETSTATES);

	gcpRendD3D->FX_SetState(GS_NODEPTHTEST);    

	CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pViewProjParam, (Vec4 *)mViewProj.GetData(), 4); 
	CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pViewProjPrevParam, (Vec4 *)mViewProjPrev.GetData(), 4); 

	CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pDiretionalBlurParamName, &cBlurVec, 1);
	CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pMotionBlurParamsName, &vParams, 1);

	// circular bokeh pattern - todo: generalize for N samples / tie with lens aperture
	Vec4 vLensSamples[12];
	
	float fRot = 10.0f;     
	Vec4 vTexelSize = Vec4(1.0f / pSceneTmp->GetWidth(), 1.0f / pSceneTmp->GetHeight(), 1.0f / pSceneTmp->GetWidth(), 1.0f / pSceneTmp->GetHeight());
	
	int nTaps = (nShaderQuality>=eSQ_High)? 8 : 4;
	float fIncAngle = 360.0 / (float) (nTaps * 2);

	for(int t = 0; t < nTaps; ++t)
	{
		vLensSamples[t] = vTexelSize * Vec4(cosf(DEG2RAD(fIncAngle *(float)t*2 + fRot) ), sinf(DEG2RAD(fIncAngle * (float)t*2 + fRot) ), 
																				cosf(DEG2RAD(fIncAngle *(float)(t*2+1)+ fRot) ), sinf(DEG2RAD(fIncAngle*(float)(t*2+1) + fRot) ));
	}

	// inner part
	if( nShaderQuality>=eSQ_High)
	{
		fIncAngle = 360.0 / (float) (4 * 2);
		for(int t = nTaps; t < nTaps + 4; ++t)
		{
			vLensSamples[t] = vTexelSize * Vec4(cosf(DEG2RAD(fIncAngle *(float)t*2 + fRot) ), sinf(DEG2RAD(fIncAngle * (float)t*2 + fRot) ), 
				cosf(DEG2RAD(fIncAngle *(float)(t*2+1)+ fRot) ), sinf(DEG2RAD(fIncAngle*(float)(t*2+1) + fRot) )) * 0.5f;
		}
	}
	
	// have to set all explicitly - workaround for constants arrays not working atm on ps3
	CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pParamLensSamplesName[0], &vLensSamples[0], 1);
	CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pParamLensSamplesName[1], &vLensSamples[1], 1);
	CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pParamLensSamplesName[2], &vLensSamples[2], 1);
	CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pParamLensSamplesName[3], &vLensSamples[3], 1);

	if( nShaderQuality>=eSQ_High )
	{
		CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pParamLensSamplesName[4], &vLensSamples[4], 1);
		CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pParamLensSamplesName[5], &vLensSamples[5], 1);
		CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pParamLensSamplesName[6], &vLensSamples[6], 1);
		CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pParamLensSamplesName[7], &vLensSamples[7], 1);

		CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pParamLensSamplesName[8], &vLensSamples[8], 1);
		CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pParamLensSamplesName[9], &vLensSamples[9], 1);
		CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pParamLensSamplesName[10], &vLensSamples[10], 1);
		CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pParamLensSamplesName[11], &vLensSamples[11], 1);
	}
		
	CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pDofFocusParam0Name, &vDofParams0, 1);
	CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pDofFocusParam1Name, &vDofParams1, 1);

	CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pMotionBlurParamName, &vMotionBlurParams, 1);

	// set radial blur parameters as well
	float fAmount = clamp_tpl<float>(m_pRadBlurAmount->GetParam(), 0.0f, 1.0f);
	float fScreenPosX = clamp_tpl<float>(m_pRadBlurScreenPosX->GetParam(), 0.0f, 1.0f);
	float fScreenPosY = clamp_tpl<float>(m_pRadBlurScreenPosY->GetParam(), 0.0f, 1.0f);
	float fRadius = m_pRadBlurRadius->GetParam();
	fRadius = 1.0f / clamp_tpl<float>(fRadius*2.0f, 0.001f, 1.0f); 

	Vec4 vRadBlurParam = Vec4(fScreenPosX, fScreenPosY, fRadius, 0.01f * fAmount);
	CShaderMan::m_shPostMotionBlur->FXSetPSFloat(m_pRadBlurParamName, &vRadBlurParam, 1);

	GetUtils().SetTexture(pSceneSrc, 0, FILTER_LINEAR);//FILTER_LINEAR); // must use point for depth in alpha
	GetUtils().SetTexture((nShaderQuality>=eSQ_High)? CTexture::s_ptexBackBufferScaled[0]:CTexture::s_ptexBackBuffer, 1, FILTER_POINT);//FILTER_LINEAR);
	GetUtils().SetTexture(CTexture::s_ptexZTarget, 2, FILTER_POINT);
	if( pDofParams.pMaskTex )
		GetUtils().SetTexture(pDofParams.pMaskTex, 3);
	
	// enable merged radial blurring mask
	CTexture *pRadBlurMaskTex = const_cast<CTexture *> (static_cast<CParamTexture*>(m_pRadialBlurMaskTex)->GetParamTexture());
	if( m_pRadBlurAmount->GetParam() > fParamThresholdBackCompatibility && pRadBlurMaskTex)
		GetUtils().SetTexture(pRadBlurMaskTex, 4);
	else
		GetUtils().SetTexture(CTexture::s_ptexWhite, 4);
					
	SD3DPostEffectsUtils::DrawFullScreenQuadWPOS(pSceneTmp->GetWidth(),	pSceneTmp->GetHeight());

  GetUtils().ShEndPass(); 

	gcpRendD3D->FX_PopRenderTarget(0); 



  
	PROFILE_LABEL_POP( "MOTION VECTOR APPLY" );
	
  // restore previous frame data
  gRenDev->m_RP.m_FlagsShader_RT = nSaveFlagsShader_RT;

	}

	gcpRendD3D->RT_SetViewport(iTempX, iTempY, iWidth, iHeight);   
	gcpRendD3D->Set2DMode(true, 1, 1);









  PROFILE_LABEL_POP( "HALF RES MOTION BLUR DOF" );
}
